package com.haixiaoke.saas.common.weChat.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import com.haixiaoke.saas.common.config.WeChatConfig;
import com.haixiaoke.saas.common.core.domain.AjaxResult;
import com.haixiaoke.saas.common.utils.GsonUtil;
import com.haixiaoke.saas.common.utils.bean.BeanUtils;
import com.haixiaoke.saas.common.weChat.domain.*;
import com.haixiaoke.saas.common.weChat.service.WeChatService;
import com.haixiaoke.saas.common.weChat.util.WeChatUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.Security;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author panda
 * @Date 2024/4/13
 **/
@Slf4j
@Service
public class WeChatServiceImpl implements WeChatService {

    @Autowired
    private WeChatConfig weChatConfig;

    /**
     * 微信统一下单
     * 微信小程序支付接口文档地址：https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
     *
     * @param unifiedOrder
     * @return
     */
    @Override
    public AjaxResult unifiedOrder(UnifiedOrder unifiedOrder) {

        if (StringUtils.isBlank(unifiedOrder.getOpenId())) {
            log.error("小程序支付：openId为空");
            return AjaxResult.error("openId为空");
        }
        try {
            String nonceStr = WeChatUtils.generateNonceStr();
            SortedMap<String, Object> paramsMap = new TreeMap<String, Object>();
            paramsMap.put("appid", unifiedOrder.getAppId());
            paramsMap.put("mch_id", unifiedOrder.getAccountId());
            paramsMap.put("nonce_str", nonceStr);
            paramsMap.put("sign_type", "MD5");
            paramsMap.put("body", unifiedOrder.getBody());
            paramsMap.put("attach", unifiedOrder.getAttach());
            paramsMap.put("out_trade_no", unifiedOrder.getOutTradeNo());
            paramsMap.put("total_fee", unifiedOrder.getTotalFee());
            paramsMap.put("spbill_create_ip", WeChatUtils.getIp());
            paramsMap.put("notify_url", unifiedOrder.getCallBackUrl());
            paramsMap.put("openid", unifiedOrder.getOpenId());
            paramsMap.put("trade_type", "JSAPI");
            // 请求参数签名
            String sign = WeChatUtils.getSign(paramsMap, unifiedOrder.getAccountApiKey());
            paramsMap.put("sign", sign);
            // 请求参数转成xml字符串
            String xmlParams = XmlUtil.mapToXmlStr(paramsMap);

            // 微信统一下单
//        String response = HttpUtil.post(weChatConfig.getUnifiedOrderUrl(), xmlParams);
            log.info("小程序支付--请求参数：{}", paramsMap);
            HttpResponse response = HttpRequest.post(weChatConfig.getUnifiedOrderUrl())
                    .body(xmlParams).timeout(20000).execute();
            Map<String, Object> responseMap = null;
            log.info("小程序支付--响应状态：{}，响应body：{}", response.getStatus(), response.body());
            if (HttpStatus.HTTP_OK == response.getStatus()) {

                responseMap = XmlUtil.xmlToMap(response.body());
                if (responseMap == null || responseMap.isEmpty()) {
                    return AjaxResult.error("支付失败，" + response.body());
                }
            }
            if (responseMap == null) {
                return AjaxResult.error("支付获取响应数据失败");
            }

            if ("SUCCESS".equals(responseMap.get("result_code")) && "SUCCESS".equals(responseMap.get("return_code"))) {
                String prepayid = responseMap.get("prepay_id").toString();

                SortedMap<String, Object> paymentUpParamMap = new TreeMap<String, Object>();
                paymentUpParamMap.put("appId", unifiedOrder.getAppId());
                paymentUpParamMap.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
                paymentUpParamMap.put("nonceStr", nonceStr);
                paymentUpParamMap.put("package", "prepay_id=" + prepayid);
                paymentUpParamMap.put("signType", "MD5");
                // 请求参数签名
                String paySign = WeChatUtils.getSign(paymentUpParamMap, unifiedOrder.getAccountApiKey());
                paymentUpParamMap.put("paySign", paySign);
                return AjaxResult.success(paymentUpParamMap);
            }
        } catch (Exception e) {
            log.error("小程序支付异常:{}", e.getMessage(), e);
        }
        return AjaxResult.error("支付失败");
    }

    @Override
    public AjaxResult refundOrder(RefundOrder refundOrder) {

        try {
            SortedMap<String, Object> parameters = new TreeMap<String, Object>();
            parameters.put("appid", refundOrder.getAppId());
            parameters.put("mch_id", refundOrder.getMchId());

            if (StringUtils.isNotBlank(refundOrder.getTransactionId())) {
                parameters.put("transaction_id", refundOrder.getTransactionId());
            }

            if (StringUtils.isNotBlank(refundOrder.getOutTradeNo())) {
                parameters.put("out_trade_no", refundOrder.getOutTradeNo());
            }

            parameters.put("nonce_str", WeChatUtils.generateNonceStr());
            parameters.put("out_refund_no", refundOrder.getOutRefundNo());
            parameters.put("total_fee", refundOrder.getTotalFee());
            parameters.put("refund_fee", refundOrder.getRefundFee());
            parameters.put("notify_url", refundOrder.getNotifyUrl());

            String sign = WeChatUtils.getSign(parameters, refundOrder.getMchApiKey());
            parameters.put("sign", sign);

            byte[] certP12 = refundOrder.getMchCertificate();
            char[] mchIdArray = refundOrder.getMchId().toCharArray();
            InputStream inputStream = new ByteArrayInputStream(certP12);
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(inputStream, mchIdArray);

            if (inputStream != null) {
                inputStream.close();
            }

            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchIdArray).build();
            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"},
                    null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
            log.info("微信退款请求参数:{}", GsonUtil.toJson(parameters));
            HttpPost httpPost = new HttpPost(weChatConfig.getRefundOrderUrl());
            httpPost.setEntity(new StringEntity(XmlUtil.mapToXmlStr(parameters), "UTF-8"));

            // 加载含有证书的http请求
            // 2023/10/16  修改jdk中的java.security，将jdk.tls.disabledAlgorithms的key删除掉SSLv3, TLSv1, TLSv1.1
            // https://www.cnblogs.com/oktokeep/p/17158735.html	第三种方法
            CloseableHttpResponse response = HttpClients.custom().setSSLSocketFactory(socketFactory).build().execute(httpPost);
            log.info("微信退款响应参数:{}", response.getEntity());
            String resultXml = EntityUtils.toString(response.getEntity(), "utf-8");
            log.info("WxPayService orderRefund = {}", resultXml);
            Map<String, String> result = WeChatUtils.doXMLParse(resultXml); // 将Xml转化为Map
            if (result != null && !result.isEmpty()) {
                if (StrUtil.equals(result.get("return_code"), "SUCCESS")) {
                    return AjaxResult.success(result);
                } else {
                    return AjaxResult.error(result.get("return_msg"));
                }
            }
        } catch (Exception e) {
            log.error("微信退款异常" + e.getMessage(), e);
        }
        return AjaxResult.error("退款异常");
    }

    @Override
    public boolean signCheck(ParseParam parseParam, String accountApiKey) {
        return WeChatUtils.signCheck(parseParam, accountApiKey);
    }

    @Override
    public RefundReqInfo getReqInfo(RefundParam refundParam) {
        try {
            // 加密数据
            String reqInfo = refundParam.getReqInfo();
            Security.addProvider(new BouncyCastleProvider());
            Security.addProvider(new BouncyCastleProvider());
            byte[] bt = Base64.decodeBase64(reqInfo);
            String md5key = DigestUtils.md5Hex(refundParam.getMchKey()).toLowerCase();
            SecretKey secretKey = new SecretKeySpec(md5key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] resultbt = cipher.doFinal(bt);
            String resultStr = new String(resultbt);

            return BeanUtils.xmlToBean(resultStr, RefundReqInfo.class);
        } catch (Exception e) {
            log.error("微信支付微信退款数据处理异常:{}", e.getMessage(), e);
        }
        return null;
    }

    /**
     * 微信扫码支付
     *
     * @param unifiedOrder
     * @return
     */
    @Override
    public AjaxResult nativeOrder(UnifiedOrder unifiedOrder) {
        try {
            String nonceStr = WeChatUtils.generateNonceStr();
            SortedMap<String, Object> paramsMap = new TreeMap<>();
            paramsMap.put("appid", unifiedOrder.getAppId());
            paramsMap.put("mch_id", unifiedOrder.getAccountId());
            paramsMap.put("nonce_str", nonceStr);
            paramsMap.put("sign_type", "MD5");
            paramsMap.put("body", unifiedOrder.getBody());
            paramsMap.put("attach", unifiedOrder.getAttach());
            paramsMap.put("out_trade_no", unifiedOrder.getOutTradeNo());
            paramsMap.put("total_fee", unifiedOrder.getTotalFee());
            paramsMap.put("spbill_create_ip", WeChatUtils.getIp());
            paramsMap.put("notify_url", unifiedOrder.getCallBackUrl());
            paramsMap.put("trade_type", "NATIVE");
            // 请求参数签名
            String sign = WeChatUtils.getSign(paramsMap, unifiedOrder.getAccountApiKey());
            paramsMap.put("sign", sign);
            // 请求参数转成xml字符串
            String xmlParams = XmlUtil.mapToXmlStr(paramsMap);

            // 微信统一下单
//        String response = HttpUtil.post(weChatConfig.getUnifiedOrderUrl(), xmlParams);
            log.info("小程序支付--请求参数：{}", paramsMap);
            HttpResponse response = HttpRequest.post(weChatConfig.getUnifiedOrderUrl())
                    .body(xmlParams).timeout(20000).execute();
            Map<String, Object> responseMap = null;
            log.info("小程序支付--响应状态：{}，响应body：{}", response.getStatus(), response.body());
            if (HttpStatus.HTTP_OK == response.getStatus()) {

                responseMap = XmlUtil.xmlToMap(response.body());
                if (responseMap == null || responseMap.isEmpty()) {
                    return AjaxResult.error("支付失败，" + response.body());
                }
            }
            if (responseMap == null) {
                return AjaxResult.error("支付获取响应数据失败");
            }
            if ("SUCCESS".equals(responseMap.get("return_code")) && "SUCCESS".equals(responseMap.get("result_code"))) {
                String codeUrl = responseMap.get("code_url").toString();
                return AjaxResult.success("操作成功",codeUrl);
            }
        } catch (Exception e) {
            log.error("小程序支付异常:{}", e.getMessage(), e);
        }
        return AjaxResult.error("支付失败");
    }

}
